package tv.geir.slackspace.code;

import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Set;
import java.util.Map.Entry;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


public class ProminentClassLoader extends ClassLoader {
	/**
	 * Logger for this class
	 */
	private static Log log = LogFactory.getLog(ProminentClassLoader.class);

	private LinkedHashMap<String, ClassDefinition> classes;

	private HashMap<String, Class> loadedClasses;
	
	public ProminentClassLoader() {
		classes = new LinkedHashMap<String, ClassDefinition>();
		loadedClasses = new HashMap<String, Class>();
		
	}

	
	
	public ProminentClassLoader(ProminentClassLoader copy) {
		if( log.isDebugEnabled() ) {
			log.debug("Copy constructor invoked, copying class definitions from other class loader");
		}
		loadedClasses = new HashMap<String, Class>();
		classes = new LinkedHashMap<String, ClassDefinition>();
		Iterator<Entry<String, ClassDefinition>> classDefs = copy.getClassDefinitions()
				.iterator();
		while (classDefs.hasNext()) {
			Entry<String, ClassDefinition> classDef = classDefs.next();
			classes.put(classDef.getKey(), classDef.getValue());
		}
	}

	protected Set<Entry<String, ClassDefinition>> getClassDefinitions() {
		return classes.entrySet();
	}

	public void addClassDefinitions(Set<Entry<String, ClassDefinition>> defs,
			boolean overwrite) {
		Iterator<Entry<String, ClassDefinition>> classDefs = defs.iterator();
		while (classDefs.hasNext()) {
			Entry<String, ClassDefinition> e = classDefs.next();

			// insert class-definitions (replaces old if existing)
			if (overwrite || !classes.containsKey(e.getKey())) {
				classes.put(e.getKey(), e.getValue());
			}
		}
	} 

	/* (non-Javadoc)
	 * @see java.lang.ClassLoader#findClass(java.lang.String)
	 */
	public synchronized Class<?> findClass(String name)
			throws ClassNotFoundException {
		if(log.isDebugEnabled()) {
			log.debug("findClass( " + name + " )");
		}
		if( name.startsWith("java." )) {
			return super.findClass( name );			
		}
		
		// check if class is already loaded
		Class c = findLoadedClass(name);
		if (c != null) {
			log.debug(name + " already loaded by this classloader");
			return c;
		}

		
		if (loadedClasses.get(name) != null) {
			log.debug(name + " already loaded, skipping");
			return loadedClasses.get(name);
		}
		
		Iterator<String> keyIterator = classes.keySet().iterator();
		while (keyIterator.hasNext()) {

			String className = keyIterator.next();
			if (className.equals(name)) {
				// yes, this is the class. define it!
				if(log.isDebugEnabled() ) {
					log.debug("Found class definition for " + name);
				}
				ClassDefinition cd = classes.get(className);
				byte[] def = cd.getDefinition();
				
				c = defineClass(null, def, 0, def.length);
				if( log.isInfoEnabled()) {
					log.info(name + " defined successfully ");
				}
				return c;

			}
		}
		log.warn("Didn't find " + name);
		throw new ClassNotFoundException(name);
	}

}
